#include "DollAnim.h"

int main(int argc, char* argv[])
{
    GLEngine* engine = new GLEngine("DollAnim",
                                    100, 100, 32, false, 0, 0);
    engine->createClient<DollAnimApp>("main");
    engine->runClient("main");
    delete engine;
}


//---------------
// Ragdoll class
//---------------

DollModel::DollModel(GLObject* obj) :
    GLRagdoll(obj)
{
    selectPart(0);
}

void DollModel::onUpdate(float dt)
{
    if (my_app->isPlaying)
    {
        updateAnim(dt);
    }
}

void DollModel::init(DollAnimApp* app)
{
    my_app = app;
    for (int i = 0; i < RP_Count; i++)
    {
        getBodyPart(i)->setSelectId(i);
        getBodyPart(i)->createBehavior<DollPart>()->init(this, i);
    }
}

void DollModel::setAction(int act)
{
    next_anim = act;
}

void DollModel::selectPart(int part)
{
    for (int i = 0; i < RP_Count; i++)
    {
        getBodyPart(i)->setColor(Color(255, 200, 14));
    }
    getBodyPart(part)->setColor(Color(14, 100, 255));
    selected_part = part;
}

//-----------
// Doll Part
//-----------

void DollPart::onRender()
{
    //coordinate system
    if (partId == doll->getSelectedPart())
    {
        float l = 0.3;
        doll->getDollApp()->axisCenter = glGetPoint2D(vector3f(0, 0, 0));
        doll->getDollApp()->axisX = glGetPoint2D(vector3f(l, 0, 0));
        doll->getDollApp()->axisY = glGetPoint2D(vector3f(0, l, 0));
        doll->getDollApp()->axisZ = glGetPoint2D(vector3f(0, 0, l));
    }
}

//-------------
// Grid Ground
//-------------

void GridGround::onRender()
{
    glBegin(GL_LINES);
    glColor3f(1, 1, 1);
    glNormal3f(0, 0, 1);
    for (int i = -size.x/2; i <= size.x/2; i++)
    {
        glVertex2f(i, -2);
        glVertex2f(i, 2);
    }
    for (int j = -size.y/2; j <= size.y/2; j++)
    {
        glVertex2f(-2, j);
        glVertex2f(2, j);
    }
    glEnd();
}


//-----------
// App Class
//-----------

DollAnimApp::DollAnimApp(GLEngine* engine) :
    GLClient(engine)
{
}

DollAnimApp::~DollAnimApp()
{
}

void DollAnimApp::onEnter()
{
    scene = new GLScene();
    scene->createBehavior<GridGround>()->init(4, 4);

    camera.zoom = -5;
    camera.rotation.set(-60, 0, 180);
    camera.position.set(0, 0, 0.6);

    selAxis = 0;
    currentFrame = 0;
    totalFrames = 4;
    animationSpeed = 100;
    showPartInfo = false;

    /*SceneBox *box = scene->createObject<SceneBox>();
    box->setScale(10, 10, 0.001);
    box->setTexture(getTexture("gfx/tile_stone.tga"));
    box->setVisible(false);*/

    ragdoll = scene->createObject<DollModel>();
    ragdoll->init(this);
    ragdoll->setAnimation(&animation);

    // !!! (create scene object without scene graph) ???
    tempdoll = new GLRagdoll(NULL);
    tempdoll->setAnimation(&animation);
    //tempdoll->setVisible(false);

    loadGuiXml("gui/animmain.xml");
    font = getGuiDesktopControl()->getFont();
    fileLabel = getGuiControl(10);
    frameLabel = getGuiControl(201);
    controlPanel = getGuiControl(200);
    playingPanel = getGuiControl(400);
    playingPanel->setVisible(false);
    playingLabel = getGuiControl(401);
    speedLabel = getGuiControl(221);

    setCurrentFrame(currentFrame);
}

void DollAnimApp::onLeave()
{
    delete tempdoll;
}

void DollAnimApp::onEvent(GLEvent e)
{
    vector3f &selPart = ragdoll->getCurrentFrame().rot[ragdoll->getSelectedPart()];
    GLObject* selModel = ragdoll->getBodyPart(ragdoll->getSelectedPart());
    GLObject* selItem = ragdoll->getBodyPart(ragdoll->getSelectedPart())->getObject();
    char fn[1024];

    switch (e.type)
    {
    case EVT_GuiClick:
        {
            if (e.gui.id >= 300 && e.gui.id < 310)
            {
                int frame = e.gui.id - 300;
                if (keyboard.key[KEY_RCTRL] || keyboard.key[KEY_LCTRL])
                {
                    //copy frame
                    animation.anim_frames[frame] = animation.anim_frames[currentFrame];
                }
                else if (keyboard.key[KEY_RSHIFT] || keyboard.key[KEY_LSHIFT])
                {
                    //shift frames
                    RagdollFrame tmp_frm = animation.anim_frames[frame];
                    animation.anim_frames[frame] = animation.anim_frames[currentFrame];
                    animation.anim_frames[currentFrame] = tmp_frm;
                }
                setCurrentFrame(frame);
            }
            else
            {
                switch (e.gui.id)
                {
                //New
                case 101:
                    animation.resetAnimation();
                    animationName = "";
                    fileLabel->setText(String("File: untitled"));
                    totalFrames = 4;
                    animationSpeed = 100;
                    break;
                //Open...
                case 102:
                    if (show_open_box(fn, "Open Animation", "*.anim|*.anim"))
                    {
                        animationName = fn;
                        animation.loadAnimation(animationName.c_str(), 1.0f, ragdoll);
                        fileLabel->setText(String("File: ") + animationName);
                        totalFrames = animation.anim_frames_count;
                        animationSpeed = 100;
                        setCurrentFrame(0);
                    }
                    break;
                //Save
                case 103:
                    if (animationName != "")
                    {
                        animation.saveAnimation(animationName.c_str(), ragdoll);
                        break;
                    }
                //Save As...
                case 104:
                    if (show_open_box(fn, "Save Animation", "*.anim|*.anim", true))
                    {
                        animationName = fn;
                        if (animationName.getSubString(animationName.getLength()-5) != ".anim")
                            animationName += ".anim";
                        animation.saveAnimation(animationName.c_str(), ragdoll);
                        fileLabel->setText(String("File: ") + animationName);
                    }
                    break;
                //frames
                case 202:
                    totalFrames--;
                    if (totalFrames < 1) totalFrames = 1;
                    setCurrentFrame(currentFrame);
                    break;
                case 203:
                    totalFrames++;
                    if (totalFrames > 10) totalFrames = 10;
                    setCurrentFrame(currentFrame);
                    break;
                //PLAY
                case 210:
                    playAnimation();
                    break;
                //STOP
                case 410:
                    stopAnimation();
                    break;
                //anim speed
                case 222:
                    animationSpeed -= 10;
                    if (animationSpeed < 10) animationSpeed = 10;
                    speedLabel->setText(String::printf("Speed: %i%%", animationSpeed));
                    break;
                case 223:
                    animationSpeed += 10;
                    if (animationSpeed > 1000) animationSpeed = 1000;
                    speedLabel->setText(String::printf("Speed: %i%%", animationSpeed));
                    break;
                }
            }
        }
        break;
    case EVT_KeyPress:
        {
            switch (e.key.code)
            {
            case KEY_ESC:
                getEngine()->quit(0);
                break;
            case KEY_L:
                camera.rotation.set(-90, 0, 90);
                break;
            case KEY_F:
                camera.rotation.set(-90, 0, 180);
                break;
            case KEY_T:
                camera.rotation.set(0, 0, 0);
                break;
            case '0':
                camera.position.set(0, 0, 0);
                break;
            case KEY_SPACE:
                if (isPlaying)
                    stopAnimation();
                else
                    playAnimation();
                break;
            case KEY_TAB:
                showPartInfo = !showPartInfo;
                break;
            //copy/paste
            case KEY_X:
                copyPart = selPart;
                selPart.reset();
                applyChanges();
                break;
            case KEY_C:
                copyPart = selPart;
                break;
            case KEY_V:
                selPart = copyPart;
                applyChanges();
                break;
            //items
            case KEY_INSERT:
                if (show_open_box(fn, "Import model", "*.obj|*.obj"))
                {
                    GLModel *m = getModel(fn);
                    selModel->createObject()->setModel(m);
                }
                break;
            case KEY_DELETE:
                selModel->destroyObjects();
                break;
            case ',':
                if (selItem)
                {
                    selItem->setScale(selItem->scale - 0.001);
                }
                break;
            case '.':
                if (selItem)
                {
                    selItem->setScale(selItem->scale + 0.001);
                }
                break;
            }
        }
        break;
    case EVT_MouseMove:
        {
            int dx = e.mouse.x - mouse.x;
            int dy = e.mouse.y - mouse.y;
            if (mouse.button[MOUSE_LEFT])
            {
                if (selAxis)
                {
                    vector2f mpos(e.mouse.x, e.mouse.y);
                    rotateSelectedPart(selAxis, lastDist - e.mouse.y);
                    lastDist = e.mouse.y;
                }
            }
            if (mouse.button[MOUSE_RIGHT])
            {
                camera.rotation.z += dx;
                camera.rotation.x += dy;
            }
            if (mouse.button[MOUSE_MIDDLE])
            {
                camera.position.z += dy * 0.01;
                camera.position.z = mClamp(camera.position.z, -1.5, 3);
            }
        }
        break;
    case EVT_MousePress:
        if (!this->getGuiHoveredControl() && !isPlaying)
        {
            //select
            if (e.mouse.button == MOUSE_LEFT)
            {
                float dmin = 5;
                float dx, dy, dz, d2;
                bool bx = v2PointToLineDistance(axisCenter, axisX, vector2f(mouse.x, mouse.y), dx, d2);
                bool by = v2PointToLineDistance(axisCenter, axisY, vector2f(mouse.x, mouse.y), dy, d2);
                bool bz = v2PointToLineDistance(axisCenter, axisZ, vector2f(mouse.x, mouse.y), dz, d2);
                if (bx && dx <= dmin)
                {
                    selAxis = 1;
                    lastDist = mouse.y;
                }
                else if (bz && dz <= dmin)
                {
                    selAxis = 3;
                    lastDist = mouse.y;
                }
                else if (by && dy <= dmin)
                {
                    selAxis = 2;
                    lastDist = mouse.y;
                }
                else
                {
                    //pick part
                    int part = scene->pickSceneObjectId(camera, mouse.x, mouse.y, 1);
                    //copy/swap parts
                    RagdollFrame &rcf = ragdoll->getCurrentFrame();
                    if (part)
                    {
                        if (keyboard.key[KEY_RCTRL] || keyboard.key[KEY_LCTRL])
                        {
                            rcf.rot[part] = selPart;
                            applyChanges();
                        }
                        else if (keyboard.key[KEY_RSHIFT] || keyboard.key[KEY_LSHIFT])
                        {
                            vector3f tmpPart = rcf.rot[part];
                            rcf.rot[part] = selPart;
                            selPart = tmpPart;
                            applyChanges();
                        }
                    }
                    //select part
                    ragdoll->selectPart(part);
                }
            }
            //cancel
            if (e.mouse.button == MOUSE_RIGHT && selAxis)
            {
                setCurrentFrame(currentFrame);
                selAxis = 0;
            }
        }
        break;
    case EVT_MouseRelease:
        if (!isPlaying)
        {
            //apply changes to animation
            if (e.mouse.button == MOUSE_LEFT && selAxis)
            {
                applyChanges();
            }
            selAxis = 0;
        }
        break;
    case EVT_MouseWheel:
        {
            camera.zoom -= e.mouse.wheel;
            if (camera.zoom > -1)
                camera.zoom = -1;
        }
        break;
    default:
        break;
    }
}

void DollAnimApp::onUpdate(float dt)
{
    if (isPlaying)
    {
        playingLabel->setText(String::printf("Frame: %0.2f / %i", ragdoll->getFramePosition(), totalFrames));
    }
    float s = animationSpeed * 0.01f;
    scene->update(dt * s);
}

void DollAnimApp::onDraw()
{
    int ww = getEngine()->getWidth();
    int hh = getEngine()->getHeight();
    Color cm[4] = { 0xFF000000, 0xFF000000, 0xFF000088, 0xFF000088 };
    rectf r(0, 0, 800, 600);
    vector3f &selPart = ragdoll->getCurrentFrame().rot[ragdoll->getSelectedPart()];

    glBeginSceneGUI(800, 600);
    glFillRectCM(r, cm);
    glEndSceneGUI();

    scene->renderScene(camera, GLRENDER_NORMAL);

    glBeginSceneGUI(ww, hh);
    if (!isPlaying)
    {
        glPushAttrib(GL_LINE_BIT);
        glLineWidth(2);
        //axis X
        if (selAxis == 0 || selAxis == 1)
        {
            glColor(Color::Red);
            glBegin(GL_LINES);
            glVertex2f(axisCenter.x, axisCenter.y);
            glVertex2f(axisX.x, axisX.y);
            glEnd();
            if (showPartInfo)
                glDrawText(font, axisX.x-30, axisX.y, String::printf("X: %0.2f", selPart.x), Color::Red);
        }
        //axis Y
        if (selAxis == 0 || selAxis == 2)
        {
            glColor(Color::Green);
            glBegin(GL_LINES);
            glVertex2f(axisCenter.x, axisCenter.y);
            glVertex2f(axisY.x, axisY.y);
            glEnd();
            if (showPartInfo)
                glDrawText(font, axisY.x-30, axisY.y, String::printf("Y: %0.2f", selPart.y), Color::Green);
        }
        //axis Z
        if (selAxis == 0 || selAxis == 3)
        {
            glColor(Color::Blue);
            glBegin(GL_LINES);
            glVertex2f(axisCenter.x, axisCenter.y);
            glVertex2f(axisZ.x, axisZ.y);
            glEnd();
            glPopAttrib();
            if (showPartInfo)
                glDrawText(font, axisZ.x-30, axisZ.y, String::printf("Z: %0.2f", selPart.z), Color::Blue);
        }
    }
    glEndSceneGUI();
}

void DollAnimApp::onGuiDraw(GLGuiControl* ctl)
{
    if (ctl->getId() >= 300 && ctl->getId() < 310)
    {
        int frame = ctl->getId() - 300;
        if (frame == currentFrame)
        {
            glFillRect(ctl->getBounds(), Color(0x80AA0000));
        }
        glPushMatrix();
        glTranslatef(ctl->getBounds().getCenter().x, ctl->getBounds().getCenter().y, 0);
        glTranslatef(0, 28, 0);
        glScalef(30, 30, 0.001);
        glRotatef(90, 1, 0, 0);
        //glRotatef(camera.rotation.x, 1, 0, 0);
        //glRotatef(camera.rotation.y, 0, 1, 0);
        glRotatef(camera.rotation.z, 0, 0, 1);
        tempdoll->calcFrame(frame);
        tempdoll->render();
        glPopMatrix();
        if (frame >= totalFrames)
        {
            glFillRect(ctl->getBounds(), Color(0xA0000000));
        }
    }
}

void DollAnimApp::setCurrentFrame(int frame)
{
    ragdoll->setFrame(frame);
    currentFrame = frame;
    animation.anim_frames_count = totalFrames;
    frameLabel->setText(String::printf("Frame: %i / %i", frame+1, totalFrames));
}

void DollAnimApp::rotateSelectedPart(int axis, float val)
{
    //GLObject* selModel = ragdoll->getBodyPart(ragdoll->getSelectedPart());
    GLObject* selItem = ragdoll->getBodyPart(ragdoll->getSelectedPart())->getObject();
    int part = ragdoll->getSelectedPart();
    bool c_mod = (keyboard.key[KEY_LCTRL] || keyboard.key[KEY_RCTRL]);
    bool s_mod = (keyboard.key[KEY_LSHIFT] || keyboard.key[KEY_RSHIFT]);
    bool a_mod = (keyboard.key[KEY_LALT] || keyboard.key[KEY_RALT]);

    if (part == RP_Root)
    {
        val *= 0.01;
    }
    switch (axis)
    {
    case 1:
        if (selItem && c_mod)
            selItem->position.x += val * 0.01;
        else if (selItem && s_mod)
            selItem->rotation.x += val;
        else if (selItem && a_mod)
            selItem->scale += val * 0.01;
        else
            ragdoll->rotatePart(part, val, 0, 0, false);
        break;
    case 2:
        if (selItem && c_mod)
            selItem->position.y += val * 0.01;
        else if (selItem && s_mod)
            selItem->rotation.y += val;
        else if (selItem && a_mod)
            selItem->scale += val * 0.01;
        else
        ragdoll->rotatePart(part, 0, val, 0, false);
        break;
    case 3:
        if (selItem && c_mod)
            selItem->position.z += val * 0.01;
        else if (selItem && s_mod)
            selItem->rotation.z += val;
        else if (selItem && a_mod)
            selItem->scale += val * 0.01;
        else
        ragdoll->rotatePart(part, 0, 0, val, false);
        break;
    }
    if (selItem && selItem->scale < 0.001f)
        selItem->scale = 0.001f;
}

void DollAnimApp::playAnimation()
{
    isPlaying = true;
    controlPanel->setVisible(!isPlaying);
    playingPanel->setVisible(isPlaying);
    ragdoll->selectPart(0);
    ragdoll->setFrame(0);
}

void DollAnimApp::stopAnimation()
{
    isPlaying = false;
    controlPanel->setVisible(!isPlaying);
    playingPanel->setVisible(isPlaying);
    setCurrentFrame(currentFrame);
}

void DollAnimApp::applyChanges()
{
    animation.anim_frames[currentFrame] = ragdoll->getCurrentFrame();
}



